#!/usr/bin/perl -w
use warnings;
use strict;
use Pod::Usage;
use Data::Dumper;

BEGIN {
    use File::Basename;
    my $path = dirname($0);
    $path = '.' unless defined $path && $path ne '';
    #print STDERR "pushing INC path: [$path]\n";
    push @INC, $path;
}

### user modules
use CPStats;
use WaferMap;
use CommandLine;
use SimpleError;
use DataAccess qw(:constants);

### constants
use constant +{
    DIM_WIDE_CELL => '28,9.5',
    DIM_HIGH_CELL => '1.8,202',
};

use constant +{
    PARAM_FS     => ',',
    PARAM_DEF    => '.',
};

### options definition
my %options = (
    'input'          => +{ option => 'input|i=s'             , value =>    undef, check => 1, },
    'repository'     => +{ option => 'repository|r=s'        , value =>    undef, check => 1, },
    'output'         => +{ option => 'output|o=s'            , value =>    undef, check => 1, },
    #-- global options
    'compare'        => +{ option => 'compare-mode|c=i'      , value =>        0, check => 1, },
    # jcap: 3'b011, klt: 3'b000, for detail, please refer to WaferMap.pm::draw_map
    'coordinate'     => +{ option => 'coordinate|C=i'        , value =>        3, check => 1, },
    'map-option'     => +{ option => 'map-option|O=i'        , value =>        1, check => 1, },
    'map-origin'     => +{ option => 'map-origin|X=s'        , value =>    '5,5', check => \&check_coll_option, },
    'wafer-field'    => +{ option => 'wafer-field|F=s'       , value =>  '4,8,1', check => \&check_coll_option, }, # Row, Col, Direction
    'cell-dimension' => +{ option => 'cell-dimension|d=s'    , value => PARAM_DEF(), check => \&check_coll_option, },
    'zoom'           => +{ option => 'zoom|z=i'              , value =>       25, check => 1, },
    'FS'             => +{ option => 'field-separator|t=s'   , value =>      ',', },
    #--
    'help'           => +{ option => 'help|h|?'              , value =>    undef, },
    'manual'         => +{ option => 'manual'                , value =>    undef, },
);
my %option_defaults = (
    'output'         => +{ reference => 'input', base => 1,
                           value => sub { my ($old_val, $ref_value, $options) = @_;
                                          return get_save_path("${ref_value}.wafermap.xls"); },
                        },
    'FS'             => +{ force => 1, value => sub {
                                                my $sep = shift; $sep =~ s/\.s/ /g;
                                                $sep =~ s/\.t/\t/g; return $sep; },
                        },
    'cell-dimension' => +{ reference => 'coordinate',
                           value => sub { my ($old_val, $coord_mode) = @_;
                                          return ($coord_mode & 4) ? DIM_WIDE_CELL() : DIM_HIGH_CELL(); },
                        },
    'map-option'     => +{ reference => 'coordinate', force => 1,
                           value => sub { my ($old_val, $coord_mode) = @_;
                                          return ($coord_mode & 4) ? $old_val : ($old_val | 0x02); },
                        },
);

### command line interface
pod2usage(-exitstatus => 0, -verbose => 1) if @ARGV == 0;
CommandLine::get_options( \%options, 'config' );
pod2usage(-exitstatus => 0, -verbose => 1) if $options{'help'}{'value'};
pod2usage(-exitstatus => 0, -verbose => 2) if $options{'manual'}{'value'};
CommandLine::apply_checks( \%options );
CommandLine::apply_defaults( \%option_defaults,  \%options, PARAM_DEF() );
#message( "options with defaults applied:" );
#CommandLine::print_options( \%options );

### main

my $da = do_input();
my $FS = $options{FS}{value};
my $output_excel = $options{output}{value};
my $repo_file = $options{repository}{value};
my $repo      = do $repo_file;
error("cannot import repository file: $repo_file: $@") if $@;
error("invalid repository format") unless check_repository($repo, $da);

### create the object
my $wafermap = WaferMap::->new();
### global options setting
$wafermap->set(Gross_Dies => $da->{Data}{Gross_Dies});
$wafermap->set(DA => $da);
$wafermap->set(SaveAs => $output_excel);

for my $i (0 .. $#$repo) {
    my $map_spec       = $repo->[$i];
    my $item           = $map_spec->{item};
    my $condition      = $map_spec->{condition};
    my $map_data       = [ [ qw(Xadr Yadr), $item ], $condition ]; # data items, condition
    my $ranges         = $map_spec->{ranges};
    my $colors         = $map_spec->{colors};
    my $sheet_name     = defined($map_spec->{worksheet})  ? $map_spec->{worksheet}  : "$item";
    my $comp_mode      = defined($map_spec->{compare})    ? $map_spec->{compare}    : $options{'compare'}{value};
    my $coord_mode     = defined($map_spec->{coordinate}) ? $map_spec->{coordinate} : $options{'coordinate'}{value};
    my $cell_dim_param = defined($map_spec->{cell})       ? $map_spec->{cell}       : $options{'cell-dimension'}{value};
    my $cell_dim       = [ split(PARAM_FS(), $cell_dim_param) ];
    my $map_option     = defined($map_spec->{option})     ? $map_spec->{option}     : $options{'map-option'}{value};
    my $map_origin     = defined($map_spec->{origin})     ? $map_spec->{origin}     : $options{'map-origin'}{value};
    my $map_zoom       = defined($map_spec->{zoom})       ? $map_spec->{zoom}       : $options{'zoom'}{value};
    my $map_origin_2   = [ split(PARAM_FS(), $map_origin) ];
    my $wafer_field    = defined($map_spec->{field})      ? $map_spec->{field}      : $options{'wafer-field'}{value};
    my ( $field_r_origin, $field_c_origin, $field_direct ) = split(PARAM_FS(), $wafer_field);
    message("add wafer map data [$i]: $sheet_name (modes: $comp_mode/$coord_mode)");
    $wafermap->add_map( +{
        Map_Data       => $map_data,
        Ranges         => $ranges,
        Colors         => $colors,
        Compare        => $comp_mode,
        Coordinate     => $coord_mode,
        Sheet_Name     => $sheet_name,
        Field_Origin   => [ $field_r_origin, $field_c_origin ],
        Field_Direct   => $field_direct,
        Map_Origin     => $map_origin_2,
        Map_Option     => $map_option,
        Column_Width   => $cell_dim->[0],
        Row_Height     => $cell_dim->[1],
        Zoom           => $map_zoom,
    } );
}

$wafermap->initialize();
$wafermap->execute();
$wafermap->finalize();
$wafermap->save();

exit 0;

### subroutines

sub check_repository {
    my ($repo, $da) = @_;
    return ref $repo eq 'ARRAY';
}

sub do_input {
    my $da = DataAccess::->new(Mode => DataAccess::DA_OVERWRITE); # always enable OVERWRITING!
    $da->retrieve($options{input}{value});
    return $da;
}

# -------------------------------------------------------------------
# utility subroutines
# -------------------------------------------------------------------

sub check_coll_option {
    my ($opts, $key) = @_;
    my $value = $opts->{$key}{value};
    use constant NUM_FIELDS => +{
        'map-origin'  => 2,
        'wafer-field' => 3,
        'cell-dimension' => 2,
    };
    my $num = NUM_FIELDS()->{$key} or error("$key: field count invalid or not existent");
    return 0 unless defined($value);
    return 1 if ($value eq PARAM_DEF());
    if ( grep( (/^\d+$/ && $_ > 0), split(',', $value) ) == $num ) {
        return 1;
    } else {
        message("option '$key': value format invalid (required fields: $num)");
        return 0;
    }
}

sub get_save_path {
    my ($save) = @_;
    if ($save !~ /^(?:\/|.:[\\\/])/) {
        $save = get_pwd() . '/' . $save;
    }
    $save;
}

sub get_pwd {
    my $pwd;
    if (`uname` =~ /linux/i) {
        $pwd = `pwd`;
    } elsif (`uname` =~ /cygwin/i) {
        $pwd = `pwd`;
        $pwd =~ s@/mnt/([a-z])/@$1:/@;
    } else {
        $pwd = `cd`;
    }
    chomp $pwd;
    $pwd =~ s/(?:^\s+|\s+$)//;
    $pwd;
}

__END__

=head1 NAME

=over 2

genwafermap

=back

=head1 SYNOPSIS

Generates wafer map.

=head1 Common Usage Scenarios

=over 2

=back

=head1 OPTIONS

=over 2

=item *

-i, --input=FILE
Specify input.

=item *

-o, --output=FILE
Specify output.

=item *

-t, --separator=STRING
Specify separating string.

=back

=head1 DESCRIPTION

=cut
